package com.atguigu.gmall.search.biz.impl;


import cn.hutool.core.util.PageUtil;
import com.atguigu.gmall.search.biz.GoodsBizService;
import com.atguigu.gmall.search.dto.SearchParamDTO;
import com.atguigu.gmall.search.entity.Goods;
import com.atguigu.gmall.search.entity.SearchAttr;
import com.atguigu.gmall.search.repository.GoodsRepository;
import com.atguigu.gmall.search.vo.SearchOrderMapVo;
import com.atguigu.gmall.search.vo.SearchRespAttrVo;
import com.atguigu.gmall.search.vo.SearchResponseVo;
import com.atguigu.gmall.search.vo.SearchTmVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.NestedQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.HighlightQuery;
import org.springframework.data.elasticsearch.core.query.HighlightQueryBuilder;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * @version: java version 1.8
 * @Author: Mr Nxl
 * @description:
 * @date: 2023-02-20 19:27
 */
@Service
@Slf4j
public class GoodsBizServiceImpl implements GoodsBizService {

    @Autowired
    private GoodsRepository goodsRepository;

    @Autowired
    private ElasticsearchRestTemplate esRestTemplate;

    @Override
    public void saveGoods(Goods goods) {

        goodsRepository.save(goods);
    }

    @Override
    public void deleteGoodsById(Long skuId) {
        goodsRepository.deleteById(skuId);
    }


    @Override
    public SearchResponseVo search(SearchParamDTO searchParamDTO) {
        log.info("GoodsBizServiceImpl...search...方法执行了...");

        //创建boolQueryBuilder,封装搜索方式
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //三级分类的搜索条件
        if (searchParamDTO.getCategory1Id() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category1Id", searchParamDTO.getCategory1Id()));
        }
        if (searchParamDTO.getCategory2Id() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category2Id", searchParamDTO.getCategory2Id()));
        }
        if (searchParamDTO.getCategory3Id() != null) {
            boolQueryBuilder.must(QueryBuilders.termQuery("category3Id", searchParamDTO.getCategory3Id()));
        }
        // 关键字搜索
        String keyword = searchParamDTO.getKeyword();
        if (!"".equals(keyword) && keyword != null) {
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", searchParamDTO.getKeyword()));
        }else {
            boolQueryBuilder.must(QueryBuilders.matchAllQuery()) ;
        }
        // 按照品牌搜索
        String trademark = searchParamDTO.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            long tmId = Integer.parseInt(trademark.split(":")[0]);
            boolQueryBuilder.must(QueryBuilders.termQuery("tmId", tmId));
        }
        // 按照平台属性进行搜索
        String[] props = searchParamDTO.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {  // props=23:4G:运行内存
                String[] split = prop.split(":");
                BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
                boolQuery.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                boolQuery.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", boolQuery, ScoreMode.None);
                boolQueryBuilder.must(nestedQuery);
            }
        }
        //创建nativeSearchQuery对象
        NativeSearchQuery nativeSearchQuery = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .build();
        // 给nativeSearchQuery设置品牌聚合的参数
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("tmIdAgg").field("tmId").size(100);
        aggregationBuilder.subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName").size(1));
        aggregationBuilder.subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl").size(1));
        nativeSearchQuery.addAggregation(aggregationBuilder);
        // 给nativeSearchQuery设置平台属性聚合的参数
        NestedAggregationBuilder nestedAggregationBuilder = AggregationBuilders.nested("attrAgg", "attrs");
        TermsAggregationBuilder attrIdAgg = AggregationBuilders.terms("attrIdAgg").field("attrs.attrId").size(200);
        attrIdAgg.subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName").size(1));
        attrIdAgg.subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue").size(100));
        nestedAggregationBuilder.subAggregation(attrIdAgg);
        nativeSearchQuery.addAggregation(nestedAggregationBuilder);

        // 设置高亮的相关参数
        if (!StringUtils.isEmpty(searchParamDTO.getKeyword() )){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title");
            highlightBuilder.preTags("<font color='red'>");
            highlightBuilder.postTags("</font>");
            HighlightQuery highlightQuery = new HighlightQuery(highlightBuilder);
            nativeSearchQuery.setHighlightQuery(highlightQuery);
        }


        //设置分页参数
        PageRequest pageable =
                PageRequest.of(searchParamDTO.getPageNo() - 1, searchParamDTO.getPageSize());
        nativeSearchQuery.setPageable(pageable);

        //排序条件 order=1:desc order=2:desc
        String sortInfo = searchParamDTO.getOrder();
        if (!StringUtils.isEmpty(sortInfo)) {
            Sort sort = null;
            String[] split = sortInfo.split(":");
            switch (split[0]) {
                case "1":
                    sort = Sort.by(split[1].equals("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "hotScore");
                    break;
                case "2":
                    sort = (Sort.by(split[1].equals("desc") ? Sort.Direction.DESC : Sort.Direction.ASC, "price"));
                    break;
            }
            nativeSearchQuery.addSort(sort);
        }


        //进行搜索
        SearchHits<Goods> goodsSearchHits = esRestTemplate.search(nativeSearchQuery, Goods.class);
        SearchResponseVo searchResponseVo = parseReponseResult(goodsSearchHits, searchParamDTO);

        return searchResponseVo;
    }

    @Override
    public void updateHotScore(Long goodsId, Integer hotScore) {
        Optional<Goods> goodsOptional = goodsRepository.findById(goodsId);
        Goods goods = goodsOptional.get();
        goods.setHotScore(Long.parseLong(String.valueOf(hotScore.intValue())));
        goodsRepository.save(goods) ;
    }


    private SearchResponseVo parseReponseResult(SearchHits<Goods> goodsSearchHits, SearchParamDTO searchParamDTO) {


        // 创建SearchResponseVo对象封装响应结果数据
        SearchResponseVo searchResponseVo = new SearchResponseVo();

        // 封装搜索条件
        searchResponseVo.setSearchParam(searchParamDTO);

        // 封装品牌面包屑
        String trademark = searchParamDTO.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            searchResponseVo.setTrademarkParam("品牌:" + trademark.split(":")[1]);
        }

        // 设置平台属性面包屑

        String[] props = searchParamDTO.getProps();
        if(props != null && props.length > 0) {
            List<SearchAttr> searchAttrList = new ArrayList<>() ;
            for(String prop : props) {
                String[] attrArray = prop.split(":");
                Long attrId = Long.parseLong(attrArray[0]) ;
                String attrValue = attrArray[1] ;
                String attrName = attrArray[2] ;
                SearchAttr searchAttr = new SearchAttr() ;
                searchAttr.setAttrId(attrId);
                searchAttr.setAttrValue(attrValue);
                searchAttr.setAttrName(attrName);
                searchAttrList.add(searchAttr) ;
            }
            searchResponseVo.setPropsParamList(searchAttrList);
        }

        // 品牌列表封装
        Aggregations aggregations = goodsSearchHits.getAggregations();
        ParsedLongTerms parsedLongTerms = aggregations.get("tmIdAgg");
        List<? extends Terms.Bucket> buckets = parsedLongTerms.getBuckets();
        List<SearchTmVo> searchTmVoList = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            //封装品牌数据

            Long tmId = Long.parseLong(bucket.getKeyAsString());
            //获取子聚合结果
            Aggregations bucketAggregations = bucket.getAggregations();
            ParsedStringTerms tmNameAgg = bucketAggregations.get("tmNameAgg");
            String tmName = tmNameAgg.getBuckets().get(0).getKeyAsString();

            // 获取品牌的logoUrl
            ParsedStringTerms tmLogoUrlAggArsedStringTerms = bucketAggregations.get("tmLogoUrlAgg");
            String tmLogoUrl = tmLogoUrlAggArsedStringTerms.getBuckets().get(0).getKeyAsString();

            //设置数据
            SearchTmVo searchTmVo = new SearchTmVo();
            searchTmVo.setTmId(tmId);
            searchTmVo.setTmName(tmName);
            searchTmVo.setTmLogoUrl(tmLogoUrl);
            // 将SearchTmVo对象存储到到searchTmVoList集合中
            searchTmVoList.add(searchTmVo);
        }
        searchResponseVo.setTrademarkList(searchTmVoList);


        // 设置排序字段
        String sortInfo = searchParamDTO.getOrder();
        SearchOrderMapVo searchOrderMapVo = new SearchOrderMapVo("1", "asc");
        if (!StringUtils.isEmpty(sortInfo) && sortInfo.contains(":")) {
            String[] split = sortInfo.split(":");
            searchOrderMapVo.setType(split[0]);
            searchOrderMapVo.setSort(split[1]);
        }
        searchResponseVo.setOrderMap(searchOrderMapVo);

        // 设置商品列表
        List<SearchHit<Goods>> searchHits = goodsSearchHits.getSearchHits();
        List<Goods> goodsList = new ArrayList<>();
        for (SearchHit<Goods> searchHit : searchHits) {
            Goods goods = searchHit.getContent();
            Map<String, List<String>> highlightFields = searchHit.getHighlightFields();
            if(highlightFields != null && highlightFields.size() > 0) {
                List<String> list = highlightFields.get("title");
                if (list != null && list.size() > 0) {
                    goods.setTitle(list.get(0));
                }
            }
            goodsList.add(goods);
        }
        searchResponseVo.setGoodsList(goodsList);
        // 设置平台属性列表
        ParsedNested attrAgg = aggregations.get("attrAgg");
        Aggregations aggAggregations = attrAgg.getAggregations();
        ParsedLongTerms attrIdAggAggregation = aggAggregations.get("attrIdAgg");
        List<? extends Terms.Bucket> idAggAggregationBuckets = attrIdAggAggregation.getBuckets();
        List<SearchRespAttrVo> searchRespAttrVos = new ArrayList<>();
        // 记录所有的平台属性的名称和平台属性的值

        for (Terms.Bucket bucket : idAggAggregationBuckets) {
            // 获取平台属性的id
            Long attrId = Long.parseLong(bucket.getKeyAsString());
            // 获取平台属性的名称
            ParsedStringTerms attrNameAgg = bucket.getAggregations().get("attrNameAgg");
            String attrName = attrNameAgg.getBuckets().get(0).getKeyAsString();
            // 获取平台属性的值
            ParsedStringTerms attrValueAgg = bucket.getAggregations().get("attrValueAgg");
            List<? extends Terms.Bucket> valueAggBuckets = attrValueAgg.getBuckets();
            List<String> attrValueList = new ArrayList<>();
            for (Terms.Bucket valueAggBucket : valueAggBuckets) {
                String attrValue = valueAggBucket.getKeyAsString();
                attrValueList.add(attrValue);
            }
            // 封装当前平台属性的名称和平台属性的值
            SearchRespAttrVo searchRespAttrVo = new SearchRespAttrVo();
            searchRespAttrVo.setAttrId(attrId);
            searchRespAttrVo.setAttrName(attrName);
            searchRespAttrVo.setAttrValueList(attrValueList);
            // 将平台属性传的名称和平台属性的值存储到List<SearchRespAttrVo>集合对象中
            searchRespAttrVos.add(searchRespAttrVo);
        }
        searchResponseVo.setAttrsList(searchRespAttrVos);
        // 设置分页参数
        searchResponseVo.setPageNo(searchParamDTO.getPageNo());
        int totalPage = PageUtil.totalPage((int) (goodsSearchHits.getTotalHits()), searchParamDTO.getPageSize().intValue());
        searchResponseVo.setTotalPages(Integer.parseInt(String.valueOf(totalPage)));
        // 构建urlParm参数
        String urlParam = getUrlParam(searchParamDTO);
        searchResponseVo.setUrlParam(urlParam);


        // 返回
        return searchResponseVo;

    }



    private String getUrlParam(SearchParamDTO searchParamDTO) {
        StringBuilder sb = new StringBuilder("list.html?");
        // 拼接分类id参数
        if (!StringUtils.isEmpty(searchParamDTO.getCategory1Id())) {
            sb.append("&category1Id=").append(searchParamDTO.getCategory1Id());
        }
        if (!StringUtils.isEmpty(searchParamDTO.getCategory2Id())) {
            sb.append("&category2Id=").append(searchParamDTO.getCategory2Id());
        }
        if (!StringUtils.isEmpty(searchParamDTO.getCategory3Id())) {
            sb.append("&category3Id=").append(searchParamDTO.getCategory3Id());
        }

        // 拼接keyword参数
        if (!StringUtils.isEmpty(searchParamDTO.getKeyword())) {
            sb.append("&keyWord=").append(searchParamDTO.getKeyword());
        }
        // 拼接品牌查询条件
        if (!StringUtils.isEmpty(searchParamDTO.getTrademark())) {
            sb.append("&trademark=").append(searchParamDTO.getTrademark());
        }
        // 拼接平台属性查询条件
        String[] props = searchParamDTO.getProps();
        if (props != null && props.length > 0) {
            for (String prop : props) {
                sb.append("&props=").append(prop);
            }
        }
        return sb.toString();
    }


}
